/* ================================================================
*   Copyright (C) 2020 All rights reserved.
*
*   文件名称：xstr_util.h
*   创 建 者：xunmenglong
*   创建日期：2020年12月02日
*   描    述：跟字符串操作相关的辅助类
*
================================================================ */


#ifndef XSTR_UTIL_H
#define XSTR_UTIL_H

#include <string.h>
#include <vector>

using namespace std;

namespace xutil {

// C风格操作的字符串辅助类
// 里面包含了一个指针和长度, 两个属性构成了一个字符串
struct xcharpos {
public:
    const char * str;
    int len;
public:
    xcharpos() {}
    xcharpos(const char * _str) {
        str = _str;
        len = strlen(_str);
    }
    xcharpos(const char * _str, int _len)
    : str(_str), len(_len) {}

    string to_string() {
        string s(str, len);
        return s;
    }
};

// 将xcharpos转换为string类
inline string build_xcharpos_str(xcharpos * char_info) {
    string s(char_info->str, char_info->len);
    return s;
}

// 将指针和长度的字符串转化为string
inline string build_xcharpos_str(const char * str, int len) {
    string s(str, len);
    return s;
}

/**
 * 将一个字符串按照某个字符切割为左右两部分
 * 因为使用的是xcharpos, 所以都是在原字符串上操作
 * 不涉及到申请新的字符串存储空间
 *
 * @param[in] char_info: 要切割的字符串
 * @param[in] s: 用于分割的字符
 * @param[out] first: 左边部分字符串
 * @param[out] second: 右边部分字符串
 *
 * @return 0成功; 其他失败
 */
inline int simple_split_by_char(xcharpos * char_info, char s,
        xcharpos * first, xcharpos * second) {
    int match_pos = -1;
    for (int i=0; i<char_info->len; i++) {
        if (char_info->str[i] == s) {
            match_pos = i;
            break;
        }
    }

    if (match_pos == -1) {
        return -1;
    }

    first->str = char_info->str;
    first->len = match_pos;

    second->str = char_info->str + (match_pos + 1);
    second->len = char_info->len - (match_pos + 1);
    return 0;
}

/**
 * 通过string类的一个比较常规的按字符串切割函数
 *
 * @param[in] str: 要被切割的字符串
 * @param[in] spliter: 用于切割匹配的字符串
 * @param[out] result: 切割完之后的字符串数组
 */
inline void split_str(const string& str, const string & spliter,
        vector<string>& result) {
    string::size_type pos1, pos2;
    pos2 = str.find(spliter);
    pos1 = 0;
    while(string::npos != pos2) {
        result.push_back(str.substr(pos1, pos2-pos1));

        pos1 = pos2 + spliter.size();
        pos2 = str.find(spliter, pos1);
    }
    if(pos1 != str.length()) {
        result.push_back(str.substr(pos1));
    }
}

/**
 * 如上的字符串分割函数使用到了大量的容器和string类
 * 有一定的性能开销, 所以实现了一个基于xcharpos的切割类
 * 该类不使用string，只在原字符串上做操作，不涉及到分配新的空间
 * 在高性能的场景下适用
 *
 * 使用方法:
 * // 单个字符串切割处理
 * xstr_spliter iter(str, spliter);
 * while (!iter.is_end()) {
 *     xcharpos cur_str = iter.get_next();
 *     ...
 * }
 *
 * // 批量字符串切割处理
 * xstr_spliter iter;
 * for ( ... ) {
 *     iter.init(str, spliter);
 *     while (!iter.is_end()) {
 *         xcharpos cur_str = iter.get_next();
 *         ...
 *     }
 * }
 */
class xstr_spliter {
private:
    const char * _str = NULL;
    const char * _spliter = NULL;
    int _split_len;
    int _pos;
    bool _is_end;

public:
    xstr_spliter(const char * str, const char * spliter) {
        init(str, spliter);
    }
    xstr_spliter() {}
    ~xstr_spliter() {}

    /**
     * 同一个对象, 可以通过init函数来切割下一个字符串
     * 降低xstr_spliter对象的申请次数
     */
    inline void init(const char * str, const char * spliter) {
        _str = str;
        _spliter = spliter;
        _split_len = strlen(spliter);
        _pos = 0;
        _is_end = false;
    }

    inline xcharpos get_next() {
        xcharpos result;
        result.str = _str + _pos;
        int start_pos = _pos;
        if (_split_len == 1) {
            while (true) {
                char c = _str[_pos];
                if (c == *_spliter) {
                    result.len = _pos-start_pos;
                    _pos++;
                    if (_str[_pos] == '\0') {
                        _is_end = true;
                    }
                    return result;
                }
                if (c == '\0') {
                    result.len = _pos-start_pos;
                    _is_end = true;
                    return result;
                }
                _pos++;
            }
        } else {
            while (true) {
                if (strncmp(_str+_pos, _spliter, _split_len) == 0) {
                    result.len = _pos-start_pos;
                    _pos += _split_len;
                    if (_str[_pos] == '\0') {
                        // 判断最后一个正好是分隔符
                        _is_end = true;
                    }
                    return result;
                }
                if (_str[_pos] == '\0') {
                    result.len = _pos-start_pos;
                    _is_end = true;
                    return result;
                }
                _pos++;
            }
        }
        return result;
    }

    inline bool is_end() {
        return _is_end;
    }
};

/**
 * 将一个字符串的前后空格去掉并输出
 * 不涉及到申请新的字符串存储空间
 *
 * @param[in] str: 要trim的字符串的指针
 * @param[in] len: 要trim的字符串的长度
 * @param[out] char_info: trim完之后的输出字符串
 */
inline void trim(const char * str, int len, xcharpos * char_info) {
    const char * ptr = str + (len-1);
    // 右边
    while (isspace(*ptr)) {
        len--;
        if (len == 0) {
            char_info->str = str;
            char_info->len = len;
            return;
        }
        ptr--;
    }

    // 左边
    ptr = str;
    while (isspace(*ptr)) {
        len--;
        if (len == 0) {
            break;
        }
        ptr++;
    }

    char_info->str = ptr;
    char_info->len = len;
}

/**
 * 将一个字符串的前后空格去掉并输出
 * 也不涉及到申请新的字符串存储空间
 *
 * @param[in] input_str: 用trim的输入字符串
 * @param[out] output_str: trim完之后的输出字符串
 */
inline void trim(xcharpos * input_str, xcharpos * out_str) {
    return trim(input_str->str, input_str->len, out_str);
}


/**
 * 判断一个字符串是否包含某个前缀
 *
 * @param[in] str: 被匹配的字符串
 * @param[in] prefix: 要匹配的前缀
 *
 * @return: true: 包含前缀; false: 不包含前缀
 */
inline bool is_starts_with(const char * str, const char * prefix) {
    return strncmp(str, prefix, strlen(prefix)) == 0;
}

/**
 * 转换json的辅助函数, 将json的特殊字符做转义
 *
 * @param[in] str: 要转换的字符串
 * @param[out] result: 转移好后的字符串
 *
 * @return 0成功; 其他失败
 */
inline int to_json_str(const char * str, string &result) {
    const char * ptr = str;
    const char * start_ptr = NULL;
    while (true) {
        char c = ptr[0];
        if (c == '\0') {
            if (start_ptr != NULL) {
                result.append(start_ptr, ptr-start_ptr);
            }
            start_ptr = NULL;
            break;
        }
        switch (c) {
            case '\"':
                if (start_ptr != NULL) {
                    result.append(start_ptr, ptr-start_ptr);
                }
                start_ptr = NULL;
                result.append("\\\"");
                break;
            case '\\':
                if (start_ptr != NULL) {
                    result.append(start_ptr, ptr-start_ptr);
                }
               start_ptr = NULL;
                result.append("\\\\");
                break;
            case '\n':
                if (start_ptr != NULL) {
                    result.append(start_ptr, ptr-start_ptr);
                }
                start_ptr = NULL;
                result.append("\\n");
                break;
            case '\r':
                if (start_ptr != NULL) {
                    result.append(start_ptr, ptr-start_ptr);
                }
                start_ptr = NULL;
                result.append("\\r");
                break;
            case '\t':
                if (start_ptr != NULL) {
                    result.append(start_ptr, ptr-start_ptr);
                }
                start_ptr = NULL;
                result.append("\\t");
                break;
            default:
                if (start_ptr == NULL) {
                    start_ptr = ptr;
                }
        }
        ptr++;
    }
    return 0;
}

/**
 * 将一个UTF8的字符串(可以包含中文)切割成单字数组
 *
 * @param[in] str: 要切割的字符串
 * @param[out] result: 输出的数组
 *
 * @return 0 成功, 其他 失败
 */
inline int split_utf8_char(const char * str, int len, vector<xcharpos> * result) {
    if (str == NULL) {
        return -1;
    }
    int i = 0;
    while (i<len) {
        char cur_char = str[i];
        if (cur_char == '\0') {
            // 结束了
            break;
        }
        int cur_char_width = 1;
        if ((cur_char & 0xE0) == 0xC0) {
            cur_char_width = 2;
        } else if ((cur_char & 0xF0) == 0xE0) {
            cur_char_width = 3;
        } else if ((cur_char & 0xF8) == 0xF0) {
            cur_char_width = 4;
        }
        xcharpos cur_wide_char;
        cur_wide_char.str = str+i;
        cur_wide_char.len = cur_char_width;
        result->push_back(cur_wide_char);
        i += cur_char_width;
    }
    return 0;
}

} // namespace xutil
#endif
